cmake_minimum_required(VERSION 3.8 FATAL_ERROR)

set(CMAKE_MODULE_PATH
  # For in-fbsource builds on mac
  "${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake"
  # For shipit-transformed builds
  "${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake"
  ${CMAKE_MODULE_PATH})

set(CMAKE_CXX_STANDARD 17)

# Tell CMake to also look in the directories where getdeps.py installs
# our third-party dependencies.
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/install")

option(BUILD_SHARED_LIBS
  "If enabled, build libraries as a shared library.  \
  This is generally discouraged, since we do not commit to having \
  a stable ABI."
  OFF
)
# Mark BUILD_SHARED_LIBS as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(BUILD_SHARED_LIBS)

enable_testing()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/external/install/include")

option(ENABLE_EDEN_SUPPORT "If enabled, add support for the Eden \
  virtual filesystem.  That requires fbthrift."
  OFF)

# Determine whether we are the git repo produced by shipit, a staging
# area produced by shipit in the FB internal CI, or whether
# we are building in the source monorepo.
# For the FB internal CI flavor running shipit, CMAKE_CURRENT_SOURCE_DIR
# will have a value like "..../shipit_projects/watchman".
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git" OR
    "${CMAKE_CURRENT_SOURCE_DIR}" MATCHES "shipit_projects")
  set(IS_SHIPPED_IT TRUE)
else()
  set(IS_SHIPPED_IT FALSE)
endif()

# If we're building from inside the monorepo, make the local directory
# look like the shipit-transformed source in the git repo.
# On windows we do a dumb recursive copy of the files because we cannot
# guarantee that we'll be successful in setting up a symlink.
# On everything else we set up a simple symlink.
# In theory we can tell cmake to add a non-child subdir and avoid the
# copy/symlink thing, but we'd need to teach various targets how to resolve
# the path and that is rather a lot of work (I spent a couple of hours on this
# before throwing in the towel).
function(maybe_shipit_dir MONOREPO_RELATIVE_PATH)
  get_filename_component(base "${MONOREPO_RELATIVE_PATH}" NAME)
  if (NOT IS_SHIPPED_IT AND
      NOT IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${base})
    if (WIN32)
      file(COPY
        "${CMAKE_CURRENT_SOURCE_DIR}/${MONOREPO_RELATIVE_PATH}"
        DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
    else()
      execute_process(COMMAND
        ln -s ${MONOREPO_RELATIVE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/${base})
    endif()
  endif()
endfunction()

if (ENABLE_EDEN_SUPPORT)
  # We use shipit to mirror in these locations from the monorepo
  maybe_shipit_dir("../eden")
endif()

set(PACKAGE_VERSION "4.9.4")
set(WATCHMAN_VERSION_OVERRIDE "" CACHE STRING "Use this version code for \
Watchman instead of the default (${PACKAGE_VERSION})")
if (WATCHMAN_VERSION_OVERRIDE)
  set(PACKAGE_VERSION "${WATCHMAN_VERSION_OVERRIDE}")
endif ()

set(PACKAGE_NAME      "watchman")
set(PACKAGE_STRING    "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME   "${PACKAGE_NAME}-${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "https://github.com/facebook/watchman/issues")
project(${PACKAGE_NAME} CXX C)
include(FBThriftCppLibrary)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckStructHasMember)
include(CheckSymbolExists)

# configure_file wants us to define a separate file.  I'd rather not
# have boilerplate for the same thing in two difference files, so we
# roll the checks in together with writing out the features to config.h
# ourselves here.
function(config_h LINE)
  file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/config.h.new" "${LINE}\n")
endfunction()

file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/config.h.new" "#pragma once\n")

if(NOT WIN32)
  set(WATCHMAN_STATE_DIR "${CMAKE_INSTALL_PREFIX}/var/run/watchman" CACHE STRING
    "Run-time path of the persistent state directory")
  set(INSTALL_WATCHMAN_STATE_DIR OFF CACHE BOOL
    "Whether WATCHMAN_STATE_DIR should be created by the cmake install
    target.  Disabling this is useful in the case where the CMAKE_INSTALL_PREFIX
    is owned by a non-privileged user but where the WATCHMAN_STATE_DIR requires
    administrative rights to create and set its permissions.")
else()
  set(WATCHMAN_STATE_DIR)
  set(INSTALL_WATCHMAN_STATE_DIR)
endif()

if(WATCHMAN_STATE_DIR AND INSTALL_WATCHMAN_STATE_DIR)
  install(DIRECTORY DESTINATION ${WATCHMAN_STATE_DIR}
    DIRECTORY_PERMISSIONS
    OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE
    GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE SETGID)
endif()

config_h("// Generated by cmake")
if(WIN32)
  config_h("#define WATCHMAN_CONFIG_FILE \
\"C:/ProgramData/facebook/watchman.json\"")
else()
  config_h("#define WATCHMAN_CONFIG_FILE \"/etc/watchman.json\"")
endif()

if(WATCHMAN_STATE_DIR)
  config_h("#define WATCHMAN_STATE_DIR \"${WATCHMAN_STATE_DIR}\"")
endif()
config_h("#define PACKAGE_VERSION \"${PACKAGE_VERSION}\"")

# While most of these tests are not strictly needed on windows, it is vital
# that we probe for and find strtoll in order for the jansson build to use
# a 64-bit integer type, otherwise the mtime_us field renders as garbage
# in the integration tests.
foreach(wat_func
    FSEventStreamSetExclusionPaths
    accept4
    backtrace
    backtrace_symbols
    backtrace_symbols_fd
    fdopendir
    getattrlistbulk
    inotify_init
    inotify_init1
    kqueue
    localeconv
    memmem
    mkostemp
    openat
    pipe2
    port_create
    statfs
    strtoll
    sys_siglist
)
  CHECK_FUNCTION_EXISTS(${wat_func} have_${wat_func})
  if (have_${wat_func})
    string(TOUPPER have_${wat_func} sym)
    config_h("#define ${sym} 1")
  endif()
endforeach(wat_func)

foreach(wat_header
    CoreServices/CoreServices.h
    execinfo.h
    fcntl.h
    inttypes.h
    locale.h
    port.h
    sys/event.h
    sys/inotify.h
    sys/mount.h
    sys/param.h
    sys/resource.h
    sys/socket.h
    sys/statfs.h
    sys/statvfs.h
    sys/types.h
    sys/ucred.h
    sys/vfs.h
    valgrind/valgrind.h
    unistd.h
)
  CHECK_INCLUDE_FILES(${wat_header} have_${wat_header})
  if (have_${wat_header})
    string(TOUPPER have_${wat_header} sym)
    string(REGEX REPLACE [./] _ sym ${sym})
    config_h("#define ${sym} 1")
  endif()
endforeach(wat_header)

CHECK_STRUCT_HAS_MEMBER(statvfs f_fstypename sys/statvfs.h
  HAVE_STRUCT_STATVFS_F_FSTYPENAME)
if (HAVE_STRUCT_STATVFS_F_BASETYPE)
  config_h("define HAVE_STRUCT_STATVFS_F_FSTYPENAME 1")
endif()

CHECK_STRUCT_HAS_MEMBER(statvfs f_basetype sys/statvfs.h
  HAVE_STRUCT_STATVFS_F_BASETYPE)
if (HAVE_STRUCT_STATVFS_F_BASETYPE)
  config_h("define HAVE_STRUCT_STATVFS_F_BASETYPE 1")
endif()

if(have_fcntl.h)
  CHECK_SYMBOL_EXISTS(O_SYMLINK fcntl.h HAVE_DECL_O_SYMLINK)
  if(HAVE_DECL_O_SYMLINK)
    config_h("#define HAVE_DECL_O_SYMLINK 1")
  endif()
endif()
find_package(PCRE)
if(PCRE_FOUND)
  config_h("#define HAVE_PCRE_H 1")
endif()

# Now close out config.h.  We only want to touch the file if the contents are
# different, so do a little dance to figure that out.
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/config.h")
  file(MD5 "${CMAKE_CURRENT_BINARY_DIR}/config.h" orig_hash)
  file(MD5 "${CMAKE_CURRENT_BINARY_DIR}/config.h.new" this_hash)
  if(NOT orig_hash STREQUAL this_hash)
    file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/config.h.new"
      "${CMAKE_CURRENT_BINARY_DIR}/config.h")
  endif()
else()
  file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/config.h.new"
    "${CMAKE_CURRENT_BINARY_DIR}/config.h")
endif()

configure_file(
  thirdparty/jansson/jansson_config.h.in
  thirdparty/jansson/jansson_config.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jansson)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

# This block is for cmake 3.0 which doesn't define the Threads::Threads
# interface section.  Test for that and define it for ourselves.
if(THREADS_FOUND AND NOT TARGET Threads::Threads)
  add_library(Threads::Threads INTERFACE IMPORTED)

  if(THREADS_HAVE_PTHREAD_ARG)
    set_property(TARGET Threads::Threads PROPERTY
      INTERFACE_COMPILE_OPTIONS "-pthread")
  endif()

  if(CMAKE_THREAD_LIBS_INIT)
    set_property(TARGET Threads::Threads PROPERTY
      INTERFACE_LINK_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
  endif()
endif()

find_package(OpenSSL)

# This block is for cmake 3.0 which doesn't define the OpenSSL::Crypto
# interface section.  Test for that and define it for ourselves.
if(OPENSSL_FOUND AND NOT TARGET OpenSSL::Crypto)
  add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
    set_target_properties(OpenSSL::Crypto PROPERTIES
      INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
    if(EXISTS "${OPENSSL_CRYPTO_LIBRARY}")
      set_target_properties(OpenSSL::Crypto PROPERTIES
        IMPORTED_LINK_INTERFACE_LANGUAGES "C"
        IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}")
    endif()
endif()

find_package(Gflags REQUIRED)
include_directories(SYSTEM ${GFLAGS_INCLUDE_DIR})

find_package(Glog REQUIRED)

# We indirectly depend on boost.  This logic needs to match
# the same criteria used in thrift, which wants static libs
# on windows.
if(MSVC)
  set(Boost_USE_STATIC_LIBS ON) #Force static lib in msvc
endif(MSVC)
find_package(
  Boost 1.54.0 REQUIRED #1.54.0 or greater
  COMPONENTS
    context
    thread
)

# We indirectly depend on libevent.  Folly pulls in linkage to
# event.lib, but on my system it does so as simply "event.lib"
# and that fails linking.  Let's probe for the library and force
# in the library directory for the linker. :-/
find_package(LibEvent REQUIRED)
get_filename_component(LIBEVENT_LIBDIR "${LIBEVENT_LIB}" DIRECTORY)
link_directories(${LIBEVENT_LIBDIR})
find_package(folly CONFIG REQUIRED)


if (ENABLE_EDEN_SUPPORT)
  find_package(fizz CONFIG REQUIRED)
  find_package(fmt CONFIG REQUIRED)
  find_package(wangle CONFIG REQUIRED)
  find_package(FBThrift CONFIG REQUIRED)
  find_package(yarpl CONFIG REQUIRED)
  find_package(rsocket CONFIG REQUIRED)
  find_package(fb303 CONFIG REQUIRED)
  include_directories(${FB303_INCLUDE_DIR})
endif()

find_package(PythonInterp REQUIRED)
message(STATUS "Found python ${PYTHON_VERSION_STRING}")

if(PYTHONINTERP_FOUND)
  set(PYOUT "${CMAKE_CURRENT_BINARY_DIR}/build/pytimestamp")
  set(SETUP_PY "${CMAKE_CURRENT_SOURCE_DIR}/python/setup.py")
  file(GLOB PYWATCHMAN_PY_SRCS "python/pywatchman/*.py")
  add_custom_command(
    COMMENT "Building pywatchman"
    OUTPUT ${PYOUT}
    DEPENDS ${PYWATCHMAN_PY_SRCS} "python/pywatchman/bser.c"
    COMMAND ${CMAKE_COMMAND} -E env
      CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
      CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
      ${PYTHON_EXECUTABLE} ${SETUP_PY} build
    COMMAND ${CMAKE_COMMAND} -E touch ${PYOUT}
  )
  add_custom_target(pybuild ALL DEPENDS ${PYOUT})
  install(CODE "
    execute_process(COMMAND
      ${CMAKE_COMMAND} -E env
        CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
      ${PYTHON_EXECUTABLE} ${SETUP_PY} install
      --root ${CMAKE_INSTALL_PREFIX}
      RESULT_VARIABLE STATUS)
    if (NOT STATUS STREQUAL 0)
      message(FATAL_ERROR \"pywatchman install failed\")
    endif()
  ")
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  # Check target architecture
  if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
    message(FATAL_ERROR "watchman requires a 64bit target architecture.")
  endif()
  add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
  include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winbuild)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /Zo /MP /MT /Oi /EHsc /GL-")
  set(CMAKE_SHARED_LINKER_FLAGS
    "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG /MT /OPT:NOREF")
  set(CMAKE_EXE_LINKER_FLAGS
    "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:NOREF")
  set(CMAKE_MODULE_LINKER_FLAGS
    "${CMAKE_MODULE_LINKER_FLAGS} /DEBUG /MT /OPT:NOREF")
  set(CMAKE_STATIC_LIBRARY_FLAGS
    "${CMAKE_STATIC_LIBRARY_FLAGS} /DEBUG /MT /OPT:NOREF")
else()
  set(CMAKE_CXX_FLAGS_COMMON "-g -Wall -Wextra -std=gnu++17")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_COMMON}")  # for cmake 3.0
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_COMMON}")
  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_COMMON} -O3")
endif()

add_library(third_party_deps INTERFACE)
target_link_libraries(third_party_deps INTERFACE
  Folly::folly
  glog::glog
  gflags
  ${Boost_LIBRARIES}
)
target_include_directories(third_party_deps INTERFACE
  ${FOLLY_INCLUDE_DIR}
  ${GLOG_INCLUDE_DIR}
  ${GFLAGS_INCLUDE_DIR}
  ${Boost_INCLUDE_DIRS}
)
if (ENABLE_EDEN_SUPPORT)
  target_link_libraries(
    third_party_deps
    INTERFACE
    ${YARPL_LIBRARIES}
    rsocket::ReactiveSocket
    FBThrift::thriftcpp2
  )
endif()
if(PCRE_FOUND)
  target_link_libraries(third_party_deps INTERFACE ${PCRE_LIBRARY})
  target_include_directories(third_party_deps INTERFACE ${PCRE_INCLUDE_DIR})
  if (WIN32)
    # The pcre headers assume that the library is a dll by default
    # but our preferred build environment only builds them as
    # static, so be sure to ask for static pcre linkage
    target_compile_definitions(third_party_deps INTERFACE PCRE_STATIC)
  endif()
endif()
target_link_libraries(third_party_deps INTERFACE Threads::Threads)
if(TARGET OpenSSL::Crypto)
  target_link_libraries(third_party_deps INTERFACE OpenSSL::Crypto)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
  target_link_libraries(third_party_deps INTERFACE "-framework CoreServices")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  target_link_libraries(third_party_deps INTERFACE
    advapi32.lib
    dbghelp.lib
    shlwapi.lib
  )
endif()

add_library(wildmatch STATIC
  thirdparty/wildmatch/wildmatch.c
  thirdparty/wildmatch/wildmatch.h
)
add_library(tap STATIC thirdparty/tap.cpp thirdparty/tap.h)
target_link_libraries(tap third_party_deps)
add_library(log STATIC PubSub.cpp log.cpp)
target_link_libraries(log third_party_deps)
add_library(hash STATIC hash.cpp)
target_link_libraries(hash third_party_deps)
add_library(err STATIC root/poison.cpp root/warnerr.cpp)
target_link_libraries(err third_party_deps)
add_library(jansson_utf STATIC thirdparty/jansson/utf.cpp)

add_library(string STATIC string.cpp)
target_link_libraries(string jansson_utf hash third_party_deps)

add_library(jansson STATIC
thirdparty/jansson/dump.cpp
thirdparty/jansson/error.cpp
thirdparty/jansson/load.cpp
thirdparty/jansson/memory.cpp
thirdparty/jansson/pack_unpack.cpp
thirdparty/jansson/strbuffer.cpp
thirdparty/jansson/strconv.cpp
thirdparty/jansson/value.cpp
)
target_link_libraries(jansson string third_party_deps)

list(APPEND testsupport_sources
ChildProcess.cpp
FileDescriptor.cpp
FileInformation.cpp
Pipe.cpp
ThreadPool.cpp
bser.cpp
cfg.cpp
expflags.cpp
ignore.cpp
opendir.cpp
pending.cpp
time.cpp
stream.cpp
)

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  list(APPEND testsupport_sources
winbuild/asprintf.cpp
winbuild/time.cpp
winbuild/backtrace.cpp
winbuild/errmap.cpp
winbuild/pathmap.cpp
winbuild/posix_spawn.cpp
stream_win.cpp
  )
endif()

add_library(testsupport STATIC ${testsupport_sources})
target_link_libraries(testsupport log string jansson third_party_deps)

if (ENABLE_EDEN_SUPPORT)
  add_fbthrift_cpp_library(
    eden_config_thrift
    eden/fs/config/eden_config.thrift
  )
  add_fbthrift_cpp_library(
    eden_service_thrift
    eden/fs/service/eden.thrift
    SERVICES
      EdenService
    DEPENDS
      eden_config_thrift
      fb303::fb303_thrift_cpp
  )
  add_fbthrift_cpp_library(
    streamingeden_thrift
    eden/fs/service/streamingeden.thrift
    SERVICES
      StreamingEdenService
    DEPENDS
      eden_service_thrift
      fb303::fb303_thrift_cpp
  )
endif()

list(APPEND watchman_sources
ChildProcess.cpp
ContentHash.cpp
CookieSync.cpp
FileDescriptor.cpp
FileInformation.cpp
InMemoryView.cpp
LocalFileResult.cpp
Pipe.cpp
# PubSub.cpp  (in liblog)
QueryableView.cpp
SymlinkTargets.cpp
ThreadPool.cpp
bser.cpp
cfg.cpp
checksock.cpp
clientmode.cpp
clockspec.cpp
error_category.cpp
expflags.cpp
fstype.cpp
groups.cpp
# hash.cpp (in libhash)
ignore.cpp
json.cpp
launchd.cpp
listener-user.cpp
listener.cpp
main.cpp
opendir.cpp
opt.cpp
pending.cpp
perf.cpp
sockname.cpp
spawn.cpp
state.cpp
stream.cpp
stream_stdout.cpp
# string.cpp (in libstring)
time.cpp
tmp.cpp
query/base.cpp
query/dirname.cpp
query/empty.cpp
query/eval.cpp
query/fieldlist.cpp
query/glob.cpp
query/intcompare.cpp
query/match.cpp
query/name.cpp
query/parse.cpp
query/pcre.cpp
query/since.cpp
query/suffix.cpp
query/type.cpp
cmds/debug.cpp
cmds/find.cpp
# cmds/heapprof.cpp
cmds/info.cpp
cmds/log.cpp
cmds/query.cpp
cmds/reg.cpp
cmds/since.cpp
cmds/state.cpp
cmds/subscribe.cpp
cmds/trigger.cpp
cmds/watch.cpp
root/ageout.cpp
root/crawler.cpp
root/dir.cpp
root/file.cpp
root/init.cpp
root/iothread.cpp
root/notifythread.cpp
# root/poison.cpp (in liberr)
root/reap.cpp
root/resolve.cpp
root/stat.cpp
root/symlink.cpp
root/sync.cpp
root/threading.cpp
root/vcs.cpp
# root/warnerr.cpp (in liberr)
root/watchlist.cpp
saved_state/LocalSavedStateInterface.cpp
saved_state/SavedStateInterface.cpp
scm/Mercurial.cpp
scm/SCM.cpp
watcher/auto.cpp
watcher/fsevents.cpp
watcher/inotify.cpp
watcher/kqueue.cpp
watcher/portfs.cpp
)

if (ENABLE_EDEN_SUPPORT)
  # We currently only support talking to eden on posix systems
  list(APPEND watchman_sources watcher/eden.cpp)
endif()

if (WIN32)
  list(APPEND watchman_sources
stream_win.cpp
watcher/win32.cpp
winbuild/errmap.cpp
winbuild/pathmap.cpp
winbuild/mkdir.cpp
winbuild/dir.cpp
winbuild/asprintf.cpp
winbuild/time.cpp
winbuild/backtrace.cpp
winbuild/getopt_long.cpp
winbuild/posix_spawn.cpp
  )
else()
  list(APPEND watchman_sources
stream_unix.cpp
  )
endif()

add_executable(watchman ${watchman_sources})
target_link_libraries(
  watchman
  log
  hash
  string
  err
  jansson
  wildmatch
  third_party_deps
)

if (ENABLE_EDEN_SUPPORT)
  target_link_libraries(
    watchman
    streamingeden_thrift
  )
endif()

install(TARGETS watchman RUNTIME DESTINATION bin)

set(tests)
# Helper function to define a unit test executable
function(t_test NAME)
  add_executable(${NAME}.t ${ARGN})
  target_link_libraries(
    ${NAME}.t
    testsupport tap wildmatch third_party_deps)
  target_compile_definitions(${NAME}.t
    PUBLIC WATCHMAN_TEST_SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")
  add_test(NAME ${NAME} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.t)
  list(APPEND tests ${NAME}.t)
endfunction()

# The `check` target runs the unit tests
add_custom_target(check
  DEPENDS ${tests}
  COMMAND ${CMAKE_CTEST_COMMAND})

if(PYTHONINTERP_FOUND)

  if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    add_executable(susres winbuild/susres.cpp)
    add_custom_target(make_susres ALL DEPENDS susres)
  endif()

  # The `integration` target runs the unit tests and integration tests
  add_custom_target(integration
    DEPENDS pybuild check
    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runtests.py
      --watchman-path ${CMAKE_CURRENT_BINARY_DIR}/watchman
      --pybuild-dir ${CMAKE_CURRENT_BINARY_DIR}/python
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()

t_test(art tests/art_test.cpp tests/log_stub.cpp)
t_test(ignore tests/ignore_test.cpp tests/log_stub.cpp)
t_test(pending tests/pending_test.cpp tests/log_stub.cpp)
t_test(string tests/string_test.cpp tests/log_stub.cpp)
t_test(log tests/log.cpp)
t_test(bser tests/bser.cpp tests/log_stub.cpp)
t_test(wildmatch tests/wildmatch_test.cpp tests/log_stub.cpp)
t_test(childproc tests/childproc.cpp tests/log_stub.cpp)
t_test(result tests/ResultTest.cpp tests/log_stub.cpp)
t_test(cache tests/CacheTest.cpp tests/log_stub.cpp)
t_test(MapUtilTest tests/MapUtilTest.cpp tests/log_stub.cpp)
